package com.spzx.product.service.impl;

import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.utils.bean.BeanUtils;
import com.spzx.common.redis.cache.GuiguCache;
import com.spzx.product.api.domain.vo.SkuLockVo;
import com.spzx.product.api.domain.vo.SkuPrice;
import com.spzx.product.api.domain.vo.SkuQuery;
import com.spzx.product.api.domain.Product;
import com.spzx.product.api.domain.vo.SkuStockVo;
import com.spzx.product.api.domain.ProductDetails;
import com.spzx.product.api.domain.ProductSku;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.mapper.ProductDetailsMapper;
import com.spzx.product.mapper.ProductMapper;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.IProductService;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 商品Service业务层处理
 */
@Service
@Transactional
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product>
implements IProductService {
    @Autowired
    ProductMapper productMapper;
    @Autowired
    private ProductSkuMapper productSkuMapper;

    @Autowired
    private ProductDetailsMapper productDetailsMapper;

    @Autowired
    private SkuStockMapper skuStockMapper;

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    RedisTemplate redisTemplate;

    /**
     * 查询商品列表
     * @param product 商品信息
     * @return 返回商品列表
     */
    @Override
    public List<Product> selectProductList(Product product) {
        return productMapper.selectProductList(product);
    }

    /**
     * 添加商品
     * @param product 商品信息
     * @return int
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int insertProduct(Product product) {
        productMapper.insert(product);
        Long productId = product.getId();

        List<ProductSku> productSkuList = product.getProductSkuList();
        int size = productSkuList.size();
        for (int i = 0; i < size; i++) {
            ProductSku productSku = productSkuList.get(i);
            productSku.setSkuCode(productId+"_"+i);
            productSku.setSkuName(product.getName() + " " + productSku.getSkuSpec());
            productSku.setProductId(productId);
            productSku.setStatus(0);
            productSkuMapper.insert(productSku);

            SkuStock skuStock = new SkuStock();
            skuStock.setSkuId(productSku.getId());
            skuStock.setTotalNum(productSku.getStockNum());
            skuStock.setAvailableNum(productSku.getStockNum());
            skuStock.setLockNum(0);
            skuStock.setSaleNum(0);
            skuStockMapper.insert(skuStock);
        }

        ProductDetails productDetails = new ProductDetails();
        productDetails.setProductId(productId);
        productDetails.setImageUrls(String.join(",",product.getDetailsImageUrlList()));
        productDetailsMapper.insert(productDetails);
        return 1;
    }

    /**
     * 获取商品详细信息
     * @param id 商品主键
     * @return 返回信息
     */
    @Override
    public Product selectProductById(Long id) {
        //商品信息
        Product product = productMapper.selectById(id);
        if (product == null){
            throw new ServiceException("数据不存在");
        }

        //商品Sku列表
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>()
                .eq(ProductSku::getProductId, id));

        //查询库存
        List<Long> skuIdList = productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());
        List<SkuStock> skuStockList = skuStockMapper.selectList(new LambdaQueryWrapper<SkuStock>()
                .in(SkuStock::getSkuId, skuIdList).select(SkuStock::getSkuId, SkuStock::getTotalNum));
        Map<Long, Integer> skuIdToStockNumMap = skuStockList.stream().collect(Collectors.toMap(SkuStock::getSkuId, SkuStock::getTotalNum));
        productSkuList.forEach(item->{
            item.setStockNum(skuIdToStockNumMap.get(item.getId()));
        });
        product.setProductSkuList(productSkuList);

        //商品详情
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>()
                .eq(ProductDetails::getProductId, id));
        product.setDetailsImageUrlList(Arrays.asList(productDetails.getImageUrls().split(",")));
        return product;
    }

    /**
     * 修改商品
     * @param product 商品信息
     * @return 返回信息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateProduct(Product product) {
        //修改商品信息
        productMapper.updateById(product);

        List<ProductSku> productSkuList = product.getProductSkuList();
        productSkuList.forEach(productSku -> {
            //修改商品Sku信息
            productSku.setSkuName(product.getName()+" "+ productSku.getSkuName());
            productSkuMapper.updateById(productSku);
            //修改库存信息
            SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>()
                    .eq(SkuStock::getSkuId, productSku.getId()));
            skuStock.setTotalNum(productSku.getStockNum());
            int availableNum = skuStock.getTotalNum() - skuStock.getLockNum();
            skuStock.setAvailableNum(availableNum);
            skuStockMapper.updateById(skuStock);
        });

        //修改商品详细信息
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>()
                .eq(ProductDetails::getProductId, product.getId()));
        productDetails.setImageUrls(String.join(",",product.getDetailsImageUrlList()));
        productDetailsMapper.updateById(productDetails);


        return 1;
    }

    /**
     * 批量删除商品
     *
     * @param ids 需要删除的商品主键集合
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class )
    @Override
    public int deleteProductByIds(List<Long> ids) {
        productMapper.deleteBatchIds(ids);

        //获取sku列表
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>()
                .in(ProductSku::getProductId, ids).select(ProductSku::getId));
        List<Long> SkuIdList = productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());
        productSkuMapper.deleteBatchIds(SkuIdList);
        skuStockMapper.delete(new LambdaQueryWrapper<SkuStock>()
                .in(SkuStock::getSkuId,SkuIdList));

        productDetailsMapper.delete(new LambdaQueryWrapper<ProductDetails>()
                .in(ProductDetails::getProductId,ids));
        return 1;
    }

    /**
     * 商品审核
     * @param id 商品主键
     * @param auditStatus 审核状态
     */
    @Override
    public void updateAuditStatus(Long id, Integer auditStatus) {
        Product product = new Product();
        product.setId(id);
        if (auditStatus == 1){
            product.setAuditStatus(1);
            product.setAuditMessage("审核通过");
        }else {
            product.setAuditStatus(-1);
            product.setAuditMessage("审核不通过");
        }
        productMapper.updateById(product);
    }

    /**
     * 更新上下架
     * @param id 商品主键
     * @param status 上下架状态
     */
    @Override
    public void updateStatus(Long id, Integer status) {
        Product product = new Product();
        product.setId(id);
        if (status == 1){
            product.setStatus(1);

            //sku加入布隆过滤器
            RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter("sku:bloom:filter");
            List<ProductSku> skuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>()
                    .eq(ProductSku::getProductId, id));
            for (ProductSku productSku : skuList) {
                bloomFilter.add(productSku.getId());
            }
        }else {
            product.setStatus(-1);
        }
        productMapper.updateById(product);
    }

    /**
     * 获得销量最好的sku
     * @return 返回sku列表
     */
    @Override
    public List<ProductSku> getTopSale() {
        return productSkuMapper.selectTopSale();
    }

    /**
     * 获取商品列表
     * @return 商品列表
     */
    @Override
    public List<ProductSku> selectProductSkuList(SkuQuery skuQuery) {
        return productSkuMapper.selectProductSkuList(skuQuery);
    }

    //=====商品详情  start===========================================

    @GuiguCache(prefix = "productSku:")
    @Override
    public ProductSku getProductSku(Long skuId) {
        ProductSku productSku = productSkuMapper.selectById(skuId);
        return productSku;
    }

    @GuiguCache(prefix = "product:")
    @Override
    public Product getProduct(Long id) {
        return productMapper.selectById(id);
    }

    @GuiguCache(prefix = "skuPrice:")
    @Override
    public SkuPrice getSkuPrice(Long skuId) {
        ProductSku productSku = productSkuMapper.selectById(skuId);
        SkuPrice skuPrice = new SkuPrice();
        BeanUtils.copyProperties(productSku,skuPrice);
        return skuPrice;
    }

    @GuiguCache(prefix = "productDetails:")
    @Override
    public ProductDetails getProductDetails(Long productId) {
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>()
                .eq(ProductDetails::getProductId, productId));
        return productDetails;
    }

    @GuiguCache(prefix = "skuSpecValue:")
    @Override
    public Map<String, Long> getSkuSpecValue(Long productId) {
        List<ProductSku> skuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>()
                .eq(ProductSku::getProductId, productId).select(ProductSku::getId,ProductSku::getSkuSpec));
        HashMap<String, Long> map = new HashMap<>();
        for (ProductSku productSku : skuList) {
            map.put(productSku.getSkuSpec(),productSku.getId());
        }
        return map;
    }

    @GuiguCache(prefix = "skuStock:")
    @Override
    public SkuStockVo getSkuStock(Long skuId) {
        SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, skuId));
        SkuStockVo skuStockVo = new SkuStockVo();
        BeanUtils.copyProperties(skuStock,skuStockVo);
        return skuStockVo;
    }

    //=====商品详情  end===========================================

    /**
     * 批量获取商品sku最新价格信息
     * @param skuIdList skuId集合
     * @return Sku价格集合
     */
    @Override
    public List<SkuPrice> getSkuPriceList(List<Long> skuIdList) {
        List<ProductSku> skuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getId, skuIdList)
                .select(ProductSku::getId, ProductSku::getSalePrice));
        return skuList.stream().map(item->{
            SkuPrice skuPrice = new SkuPrice();
            skuPrice.setSkuId(item.getId());
            skuPrice.setSalePrice(item.getSalePrice());
            return skuPrice;
        }).toList();
    }

    /**
     * 检查与锁定库存
     * @param orderNo 订单号
     * @param skuLockVoList 锁库存实体
     * @return 返回结果
     */
    @Override
    public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {

        String dataKey = "sku:lock:data:" + orderNo; //用于存储锁定库存数据
        String lockKey = "sku:checkAndLock:lock:"+orderNo;
        //去重处理
        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, orderNo, 1, TimeUnit.HOURS);
        if (!ifAbsent){
            if (redisTemplate.hasKey(dataKey)){
                return "";
            }else {
                return "请勿重复提交";
            }
        }

        //遍历所有商品，对每一个商品进行库存检查(增加行锁 for update,不允许别的事务进行数据操作。)
        for (SkuLockVo skuLockVo : skuLockVoList) {
            SkuStock skuStock = skuStockMapper.check(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
            if (skuStock == null){ //数据不存在或库存不够
                skuLockVo.setIsHaveStock(false);
            }else {
                skuLockVo.setIsHaveStock(true);
            }
        }

        //判断是否存在库存不够的商品，如果存在库存不够的商品情况，那么，返回特定字符串消息即可；
        if(skuLockVoList.stream().anyMatch(skuLockVo -> !skuLockVo.getIsHaveStock())){
            List<SkuLockVo> noHaveskuLockVoList = skuLockVoList.stream().filter(skuLockVo -> !skuLockVo.getIsHaveStock()).toList();
            StringBuffer result = new StringBuffer();
            for (SkuLockVo skuLockVo : noHaveskuLockVoList) {
                result.append("商品:["+skuLockVo.getSkuId()+"]库存不够");
            }
            redisTemplate.delete(lockKey);
            return result.toString();
        }else{
            //如果所有库存检查没问题，商品库存都够。那么才会锁定库存，不让别人买这个商品。
            for (SkuLockVo skuLockVo : skuLockVoList) {
                int row = skuStockMapper.lock(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
                if (row == 0){
                    redisTemplate.delete(lockKey);
                    throw new ServiceException("锁库存失败");
                }
            }
        }

        //把锁定库存信息保存到redis中，不设置过期时间，等着  解锁库存(取消订单)  或   减库存(支付成功)
        redisTemplate.opsForValue().set(dataKey,skuLockVoList);
        return "";
    }

    /**
     * 解锁库存
     * @param orderNo 订单号
     */
    @Override
    public void unlock(String orderNo) {
        String dataKey = "sku:lock:data:" + orderNo; //用于存储锁定库存数据
        String lockKey = "sku:unlock:lock:"+orderNo;
        //去重处理
        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, orderNo, 1, TimeUnit.HOURS);
        if (!ifAbsent){
            return;
        }

        // 获取锁定库存的缓存信息
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(dataKey);
        if (CollectionUtils.isEmpty(skuLockVoList)){
            return;
        }

        //解锁库存
        for (SkuLockVo skuLockVo : skuLockVoList) {
            int row = skuStockMapper.unlock(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
            if (row == 0){
                redisTemplate.delete(lockKey);
                throw new ServiceException("解锁失败");
            }
        }
        //活干完后删除缓存数据; 避免重复解锁 或者 重复减库存
        redisTemplate.delete(dataKey);
    }

    /**
     * 扣减库存
     * @param orderNo  订单号
     */
    @Override
    public void minus(String orderNo) {
        String dataKey = "sku:lock:data:" + orderNo; //用于存储锁定库存数据
        String lockKey = "sku:minus:lock:"+orderNo;
        //去重处理
        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, orderNo, 1, TimeUnit.HOURS);
        if (!ifAbsent){
            return;
        }

        // 获取锁定库存的缓存信息
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(dataKey);
        if (CollectionUtils.isEmpty(skuLockVoList)){
            return;
        }

        //解锁库存
        for (SkuLockVo skuLockVo : skuLockVoList) {
            int row = skuStockMapper.minus(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
            if (row == 0){
                redisTemplate.delete(lockKey);
                throw new ServiceException("扣减库存失败");
            }
        }
        //活干完后删除缓存数据; 避免重复解锁 或者 重复减库存
        redisTemplate.delete(dataKey);
    }
}
